摘要
文章首先概述了專案的核心目標,包括 理解自然語言查詢、進行即時網路搜尋、提供準確相關的答案、支援多輪對話、提供流暢的使用體驗。接著,文章深入分析了系統的架構,將系統劃分為 前端、後端和核心處理層 三個部分,並詳細介紹了每個部分的功能和使用的技術。核心處理層使用 LangGraph 來理解使用者問題,並決定如何回應。前端使用 Streamlit 來提供一個直覺的使用者介面。後端則使用 FastAPI 和 LangServe 來處理請求並與核心處理層通訊。
文章的後半部分深入探討了 LangGraph 的實作,包括 圖形結構、狀態管理、節點結構、FastAPI 整合和 Streamlit 前端實作 等方面。這些內容展示了 LangGraph 如何允許建立複雜的對話系統,如何透過狀態管理維護對話的連續性,以及如何使用 Streamlit 建立一個與 LangGraph 系統互動的使用者介面。
在當今資訊爆炸的時代,快速獲取準確、相關的資訊變得越來越具有挑戰性。為了應對這一挑戰,我們開發了一個創新的 Web 搜尋助手,它結合了最先進的自然語言處理技術和直覺的使用者介面設計。這個專案旨在為使用者提供一個智慧、互動且友善的平台,使他們能夠輕鬆地進行網路搜尋並獲得高品質的回答。
我們的 Web 搜尋助手不僅僅是一個簡單的搜尋引擎,它是一個能夠理解上下文、進行多輪對話,並能夠利用外部工具來增強其能力的智慧系統。透過整合 LangGraph、FastAPI 和 Streamlit 等先進技術,我們創造了一個強大而靈活的解決方案,能夠滿足各種複雜的資訊需求。
本專案的核心目標是創建一個智慧的 Web 搜尋助手,它能夠:
你有沒有想過,當你問一個智慧助理問題時,背後究竟發生了什麼事?讓我們一起揭開這個神祕的面紗,如何?
在深入探討各個元件之前,讓我們先對整個系統有一個宏觀的認識。我們的 Web 搜尋助手系統主要由三個核心部分組成:前端、後端和核心處理層。
前端使用 Streamlit 建構,提供了一個直覺、互動的使用者介面。後端由 FastAPI 和 LangServe 組成,負責處理請求並與核心處理層通訊。核心處理層則是由 LangGraph 驅動,負責自然語言理解、決策制定和工具呼叫。
首先,我們的系統分為三個主要部分。你能猜到是哪三個嗎?沒錯,就是前端、後端和核心處理層。我們一個個來看:
前端 - 你眼前的這個介面:
首先是前端,也就是你眼前看到的這個介面。我們使用了 Streamlit 來打造這個部分。它就像一個親切的櫃檯人員,隨時準備接收你的問題,並將結果呈現給你。Streamlit 的特點是簡潔易用,能夠快速呈現互動式的網頁介面。
後端 - 幕後的大腦:
接著是後端,這裡有兩個重要角色:FastAPI 和 LangServe。FastAPI 負責接收前端傳來的請求,它的特點是處理速度快,而且支援非同步操作。LangServe 則是負責將請求轉換成 LangGraph 可以理解的格式。這兩個元件就像是效率超高的秘書,確保訊息能夠順利地在系統內部傳遞。
核心處理層 - LangGraph:
最後是核心處理層,也就是我們的「大腦」- LangGraph。它主要由兩個部分組成:Chatbot 節點和 Tool 節點。Chatbot 節點負責理解使用者的問題,決定如何回應。如果需要額外資訊,它會向 Tool 節點求助。Tool 節點則可以連接外部資源,比如搜尋引擎或資料庫,來獲取所需的資訊。
現在,讓我們跟隨一個問題的旅程。假設你問:「最近的 AI 突破是什麼?」
這個問題首先會被 Streamlit 前端接收,然後打包成一個請求傳送到後端。FastAPI 接收到這個請求後,會將它交給 LangServe 進行必要的格式轉換。接著,問題就進入了 LangGraph。
在 LangGraph 中,Chatbot 節點首先會分析你的問題。它可能會發現這是一個需要最新資訊的問題,於是決定使用 Tool 節點來獲取最新的 AI 相關新聞。Tool 節點可能會使用預先設定好的搜尋 API 來查詢相關資訊。獲得資訊後,Chatbot 節點會整理這些資料,組織成一個清晰、易懂的回答。
完成處理後,回答會再次經過 LangServe 和 FastAPI,最後回到 Streamlit 前端,呈現在你的眼前。
LangGraph 是一個強大的框架,允許我們創建複雜的對話系統。讓我們逐步分析其核心元件。
現在讓我們深入分析 graph.py
中的 LangGraph 實作:
# 創建圖形建構器
graph_builder = StateGraph(State)
# 新增節點
graph_builder.add_node(CHATBOT_NODE, chatbot)
graph_builder.add_node(TOOLS_NODE, tool_node)
# 新增邊
graph_builder.add_edge(START, CHATBOT_NODE)
# 新增條件邊
graph_builder.add_conditional_edges(
CHATBOT_NODE,
tools_condition,
)
graph_builder.add_edge(TOOLS_NODE, CHATBOT_NODE)
# 編譯圖形
graph = graph_builder.compile()
這個模組整合了所有元件,構建了完整的 LangGraph 結構:
TOOLS_NODE
, CHATBOT_NODE
)增強可讀性。add_conditional_edges
)實作動態決策流程。tools_condition
函式決定是否需要使用工具。如果需要,流程會轉到 TOOLS_NODE;否則,它會繼續在 CHATBOT_NODE。
這種結構允許系統根據需要靈活地在對話和工具使用之間切換。例如,當使用者詢問需要最新資訊的問題時,系統可以自動切換到搜尋工具,然後將結果返回給聊天機器人進行處理。
透過這種方式,我們創建了一個動態的、上下文感知的對話系統,能夠根據對話的需求靈活地使用外部工具和資源。
讓我們看看 state.py
檔案,它定義了系統的狀態管理:
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
這個簡單但強大的定義有以下幾個關鍵點:
TypedDict
確保型別安全,這在大型專案中尤為重要。Annotated[list, add_messages]
是一個巧妙的設計,它允許我們輕鬆地向狀態中新增新訊息。messages
欄位將儲存整個對話歷史,使系統能夠保持上下文感知。這種狀態設計允許我們在整個圖中維護對話的連續性,是實現多輪對話的關鍵。
接下來,讓我們看看系統中的兩個主要節點:工具節點和聊天機器人節點。
import os
from app.core.config import settings
from langgraph.prebuilt import ToolNode
from langchain_community.tools.tavily_search import TavilySearchResults
# 創建 Tavily 搜尋工具實例
tool = TavilySearchResults(max_results=2)
# 使用 LangGraph 的 ToolNode 封裝工具
tool_node = ToolNode(tools=[tool])
這個模組專注於設置和配置搜尋工具:
TavilySearchResults
創建一個網路搜尋工具。max_results=2
限制了每次搜尋返回的結果數量,有助於控制資訊量。ToolNode
封裝工具,使其容易整合到圖中。llm = ChatOpenAI(
temperature=0,
max_tokens=512,
timeout=None,
max_retries=2,
)
llm_with_tools = llm.bind_tools([tool])
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
這個模組負責聊天機器人的核心邏輯:
LangServe 是一個強大的工具,它簡化了將 LangChain 和 LangGraph 應用程式部署為 API 的過程。在 main.py
中,我們可以看到 LangServe 的使用:
from langserve import add_routes
from app.agents.search_agent.graph import graph
# ... (其他 FastAPI 設置程式碼)
add_routes(
app,
graph,
path="/websearch",
)
這段程式碼的作用是:
在本機執行以下指令
./run_local.sh
在我們深入了解 Streamlit 前端的具體實作之前,讓我們先回顧一下我們的系統架構。我們已經成功地使用 LangGraph 構建了一個強大的對話式搜尋引擎,並透過 FastAPI 和 LangServe 將其暴露為 API。現在,我們將使用 Streamlit 創建一個使用者友善的介面,使最終使用者能夠輕鬆地與我們的系統進行互動。
Streamlit 是一個極其強大且易用的 Python 函式庫,專為創建資料應用程式和機器學習模型的互動式 Web 介面而設計。在我們的案例中,它將成為連接使用者和我們複雜後端系統的橋樑。透過 Streamlit,我們將能夠快速構建一個直覺的聊天介面,即時顯示對話,並無縫地與我們的 LangServe API 進行通訊。
讓我們逐步解析這個 Streamlit 應用程式的實作:
首先,我們需要匯入必要的函式庫和設置基本配置:
import streamlit as st
from langchain.schema import HumanMessage, AIMessage
from langserve import RemoteRunnable
from typing import Optional
API_URL = "http://localhost:8000/websearch/"
app = RemoteRunnable(API_URL)
這部分程式碼匯入了必要的函式庫和類別,並設置了 LangServe 服務的 URL。RemoteRunnable
允許我們與遠端 LangServe API 進行互動。
接下來,我們定義了一個關鍵函式,用於處理來自 LangServe 的串流回應:
def stream_app_catch_tool_calls(inputs, thread) -> Optional[AIMessage]:
tool_call_message = None
assistant_response = ""
for output in app.stream(inputs, thread, stream_mode="values"):
for key, value in output.items():
message = value["messages"][-1]
if isinstance(message, AIMessage) and message.tool_calls:
tool_call_message = message
else:
if isinstance(message, AIMessage):
assistant_response += message.content
st.write(message.content)
return tool_call_message, assistant_response
這個函式負責串流處理來自 LangServe 的回應。它捕捉工具呼叫(如果有的話)並累積助理的回應。使用Streamlit 的 st.write()
即時顯示助理的回應。
現在,讓我們開始構建我們的使用者介面:
st.title('網路查詢助手')
st.markdown("---")
st.markdown("提示: 您可以詢問各種問題,例如 '2024 鐵人賽關於 LangGraph 文章' 或 '高雄推薦餐廳有哪些?'")
if 'conversation_history' not in st.session_state:
st.session_state.conversation_history = []
這部分設置了 Streamlit 應用的標題,新增了使用提示,並初始化了對話歷史。使用 st.session_state
來儲存對話歷史,確保在頁面重新整理時不會遺失。
接下來,我們將深入探討如何顯示對話歷史,處理使用者輸入,以及如何將所有這些元素整合成一個流暢的使用者體驗。
for message in st.session_state.conversation_history:
with st.chat_message(message["role"]):
st.write(message["content"])
這個迴圈遍歷對話歷史,使用 Streamlit 的 st.chat_message()
函式以聊天介面的形式顯示每條訊息。這種方法創建了一個視覺上吸引人的對話流,使使用者可以輕鬆回顧整個對話過程。透過使用 st.session_state
,我們確保即使在頁面重新整理後,對話歷史也能保持不變,提供了持續的使用者體驗。
user_input = st.chat_input("請輸入您的查詢:")
if user_input:
st.session_state.conversation_history.append({"role": "user", "content": user_input})
with st.chat_message("user"):
st.write(user_input)
這部分程式碼負責捕捉和處理使用者輸入。st.chat_input()
創建了一個聊天風格的輸入框,與整體介面風格保持一致。當使用者提交查詢時,我們立即將其新增到對話歷史中,並使用 st.chat_message("user")
在介面上顯示。這種即時回饋增強了應用的互動性,讓使用者感覺他們正在進行真實的對話。
with st.chat_message("assistant"):
with st.spinner("正在處理您的查詢..."):
try:
thread = {"configurable": {"thread_id": "5"}}
inputs = [HumanMessage(content=user_input)]
tool_call_message, assistant_response = stream_app_catch_tool_calls(
{"messages": inputs},
thread
)
if assistant_response:
st.session_state.conversation_history.append({"role": "assistant", "content": assistant_response})
else:
st.error("抱歉,我無法處理您的查詢。請稍後再試。")
st.session_state.conversation_history.append({"role": "assistant", "content": "抱歉,我無法處理您的查詢。請稍後再試。"})
except Exception as e:
st.error(f"發生錯誤: {e}")
st.session_state.conversation_history.append({"role": "assistant", "content": f"發生錯誤: {e}"})
這是應用程式的核心處理部分,它負責與後端 API 互動並處理回應:
st.rerun()
使用 st.rerun()
強制 Streamlit 重新執行應用,這確保了最新的對話內容被即時顯示在介面上。這個簡單但重要的步驟保證了使用者總是看到最新的對話狀態。
要啟動Streamlit應用,請按照以下步驟操作:
pip install streamlit
streamlit_app.py
,並執行以下指令:streamlit run streamlit_app.py
在本文中,我們詳細介紹了一個基於 LangGraph、FastAPI 和 Streamlit 的智慧 Web 搜尋助手系統。這個系統成功地整合了先進的自然語言處理技術、高效的後端服務和直覺的使用者介面,為使用者提供了一個強大而易用的資訊檢索工具。
透過 LangGraph 的靈活圖形結構,我們實現了複雜的對話管理和動態工具呼叫。FastAPI 和 LangServe 的結合為系統提供了高效、可擴展的後端支援。而 Streamlit 前端則確保了使用者能夠輕鬆地與系統進行互動。
即刻前往教學程式碼 Repo,親自搭建屬於自己的 LLM App 吧!別忘了給專案按個星星並持續關注更新,讓我們一起探索AI代理的新境界。
#參考資料: